package pso.gui.visualization;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;

import javax.swing.SwingUtilities;

import pso.PSOAlgorithm;
import pso.particle.Particle;
import evolution.problem.OptimizationProblem;
import evolution.problem.ProblemConstraints;

public class SwarmVisualizationPanel extends HeatMap implements
		PropertyChangeListener {

	private static final long serialVersionUID = 3999536339162606968L;
	private Point[] points = {};
	private ParticleToPointConverter helper = new ParticleToPointConverter();
	private ProblemConstraints problemConstraints;
	private int sleepTime;

	public static SwarmVisualizationPanel buildPanel(OptimizationProblem problem) {
		double[][] data = HeatMapDataGenerator
				.generateHeatMapData(problem, 300);
		boolean useGraphicsYAxis = false;
		SwarmVisualizationPanel panel = new SwarmVisualizationPanel(data,
				useGraphicsYAxis, Gradient.GRADIENT_MAROON_TO_GOLD);
		panel.problemConstraints = problem.getConstraints();
		double maxArg = problem.getConstraints().getMaxArg();
		double minArg = problem.getConstraints().getMinArg();
		panel.setCoordinateBounds(minArg, maxArg, minArg, maxArg);
		panel.setDrawXTicks(true);
		panel.setDrawYTicks(true);
		panel.setToolTipText("Cząsteczki stada nadrzędnego");
		return panel;
	}

	private SwarmVisualizationPanel(double[][] data, boolean useGraphicsYAxis,
			Color[] colors) {
		super(data, useGraphicsYAxis, colors);
	}

	public void paintComponent(Graphics g) {
		super.paintComponent(g);
		for (Point point : points) {
			// Point point = helper.convertToPoint(p);
			g.fillRect(point.x - 1, point.y - 1, 3, 3);
		}
	}

	public void setParticles(Particle[] particles) {
		this.points = helper.convertToPoints(particles);
		if (SwingUtilities.isEventDispatchThread()) {
			repaint();
		} else {
			SwingUtilities.invokeLater(new Runnable() {

				@Override
				public void run() {
					repaint();
				}
			});
		}
	}

	@Override
	public void propertyChange(PropertyChangeEvent evt) {
		PSOAlgorithm psoAlgorithm = (PSOAlgorithm) evt.getSource();
		setParticles(psoAlgorithm.getSwarm());
		if (sleepTime > 0) {
			try {
				Thread.sleep(sleepTime);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}

	public int getSleepTime() {
		return sleepTime;
	}

	public void setSleepTime(int sleepTime) {
		this.sleepTime = sleepTime;
	}

	public class ParticleToPointConverter {
		public static final int MARGIN_SIZE = 30; // heat map margin in pixels

		public Point[] convertToPoints(Particle[] particles) {
			int length = particles.length;
			Point[] points = new Point[length];

			int totalHeight = getHeight();
			int totalWidth = getWidth();

			double minArg = problemConstraints.getMinArg();
			double maxArg = problemConstraints.getMaxArg();

			for (int i = 0; i < length; i++) {
				double particleX = particles[i].getPosition()[0];
				double particleY = particles[i].getPosition()[1];

				double relativeX = (particleX - minArg) / (maxArg - minArg);
				double relativeY = 1 - (particleY - minArg) / (maxArg - minArg);

				double x = relativeX * (totalWidth - 2 * MARGIN_SIZE)
						+ MARGIN_SIZE;
				double y = relativeY * (totalHeight - 2 * MARGIN_SIZE)
						+ MARGIN_SIZE;

				Point point = new Point((int) x, (int) y);
				points[i] = point;
			}

			return points;
		}
	}
}
